[Linux-Kernel][紀錄Process Context Switch次數和idle time]

紀錄Process Context Switch次數和idle time

  1. 在linux/sched.h中的struct task_struct的最後(”#endif”之後,”};”之前)加入

    linux/sched.h
    1
    2
    unsigned int cs_count;
    struct timeval time_value;

    如果將該變數加到task_struct中的任意位置,會產生問題,因為context switch是靠固定的位移記憶體大小來得到task_struct其中的變數,交換process的。

  2. 在linux/sched.h中加入

    linux/sched.h
    1
    2
    3
    4
    5
    struct process_switch_info
    {
    unsigned int counter_cpu, counter_IO;
    struct timeval time_cpu, time_IO;
    };

    為了讓之後的systemcall-get_process_switch_info(struct process_switch_info *, int, int)可以傳入struct process_switch_info的變數

  3. 在kernel/fork.c中return p之前加入

    kernel/fork.c
    1
    2
    3
    p->cs_count=0;
    p->time_value.tv_sec=0;
    p->time_value.tv_usec=0;

    將其值初始化

  4. 在kernel/sched/core.c中 context_switch(rq,prev,next)前後加上

    kernel/sched/core.c
    1
    2
    3
    4
    5
    6
    7
    8
    struct timeval now_value,future_value;
    next->cs_count++;
    do_gettimeofday(&now_value);
    context_switch(rq, prev, next); /* unlocks the rq */
    do_gettimeofday(&future_value);
    prev->time_value.tv_sec+=future_value.tv_sec-now_value.tv_sec;
    prev->time_value.tv_usec+=future_value.tv_usec-now_value.tv_usec;
    prev->cs_count++;

    在context switch之前 next的計數器+1,因為要準備switch過去next了;在context switch之後prev的計數器+1,因為已經switch回來了;另外加入now_value和future_value來記錄context switch前和後的時間,這樣就可以知道context switch其中的idle time了

  5. 記得在include/linux/syscall.h裡面#include
    因為system call裡面會用到在sched.h宣告的struct process_switch_info
    如果在其他地方宣告struct process_switch_info,即要換成include那個

  6. 在arch/x86/kernel新增gpsi.c,system call - get_process_switch_info如下

    arch/x86/kernel/gpsi.c
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    #include <linux/linkage.h>
    #include <linux/kernel.h>
    #include <linux/sched.h>
    #include <linux/mm_types.h>
    #include <linux/fs.h>
    #include <linux/mm.h>
    #include <linux/time.h>
    asmlinkage void get_process_switch_info(struct process_switch_info * psi, int pid1, int pid2)
    {
    struct process_switch_info* info;
    info=psi;
    struct task_struct *task_list;
    struct task_struct *process1;
    struct task_struct *process2;
    for_each_process(task_list)
    {
    if(task_list->pid==pid1)
    {
    process1=task_list;
    }
    else if(task_list->pid==pid2)
    {
    process2=task_list;
    }
    }
    if(process1&&process2)
    {
    info->counter_cpu =process1->cs_count;
    info->counter_IO =process2->cs_count;
    info->time_cpu.tv_sec =process1->time_value.tv_sec;
    info->time_cpu.tv_usec =process1->time_value.tv_usec;
    info->time_IO.tv_sec =process2->time_value.tv_sec;
    info->time_IO.tv_usec =process2->time_value.tv_usec;
    }
    else
    {
    printk("\nprocess1(PID:%d) or process2(PID:%d) not found\n",pid1,pid2);
    return 0;
    }
    return 0;
    }
  7. make modules_install

  8. make install
  9. 編譯test_process_switch.c並執行
    會發現可以輸出context switch的次數了,和idle time了
  10. 最後要知道 限制3分鐘之內 context switch的次數和idle time,將core.c改成
    kernel/sched/core.c
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    struct timeval now_value,future_value;
    if((next->start_time+180000000000)>ktime_get_ns())
    {
    next->cs_count++;
    }
    do_gettimeofday(&now_value);
    context_switch(rq, prev, next); /* unlocks the rq */
    do_gettimeofday(&future_value);
    if((prev->start_time+180000000000)>ktime_get_ns())
    {
    prev->time_value.tv_sec+=future_value.tv_sec-now_value.tv_sec;
    prev->time_value.tv_usec+=future_value.tv_usec-now_value.tv_usec;
    prev->cs_count++;
    }

    在count和idle time外圍增加時間限制,當start_time超過三分鐘,即不count,也不增加idle time
    ktime_get_ns()為得到目前cpu的時間,單位為ns,task_struct->start_time的單位也是ns,所以要以同一單位來度量,3分鐘即為180000000000ns
    ktime_get_ns()和getnstimeofday()有什麼區別
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    ktime_t ktime_get(void)
    {
    struct timekeeper *tk = &tk_core.timekeeper;
    unsigned int seq;
    ktime_t base;
    s64 nsecs;
    WARN_ON(timekeeping_suspended);
    do {
    seq = read_seqcount_begin(&tk_core.seq);
    base = tk->tkr.base_mono;
    nsecs = timekeeping_get_ns(&tk->tkr);
    } while (read_seqcount_retry(&tk_core.seq, seq));
    return ktime_add_ns(base, nsecs);
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int __getnstimeofday64(struct timespec64 *ts)
{
struct timekeeper *tk = &tk_core.timekeeper;
unsigned long seq;
s64 nsecs = 0;
do {
seq = read_seqcount_begin(&tk_core.seq);
ts->tv_sec = tk->xtime_sec;
nsecs = timekeeping_get_ns(&tk->tkr);
} while (read_seqcount_retry(&tk_core.seq, seq));
ts->tv_nsec = 0;
timespec64_add_ns(ts, nsecs);
/*
* Do not bail out early, in case there were callers still using
* the value, even in the face of the WARN_ON.
*/
if (unlikely(timekeeping_suspended))
return -EAGAIN;
return 0;
}

兩個差別最大應該在於
base = tk->tkr.base_mono;

ts->tv_sec = tk->xtime_sec;
看看這兩者差在哪

in-kernel/time/timekeeping.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static inline void tk_update_ktime_data(struct timekeeper *tk)
{
s64 nsec;
/*
* The xtime based monotonic readout is:
* nsec = (xtime_sec + wtm_sec) * 1e9 + wtm_nsec + now();
* The ktime based monotonic readout is:
* nsec = base_mono + now();
* ==> base_mono = (xtime_sec + wtm_sec) * 1e9 + wtm_nsec
*/
nsec = (s64)(tk->xtime_sec + tk->wall_to_monotonic.tv_sec);
nsec *= NSEC_PER_SEC;
nsec += tk->wall_to_monotonic.tv_nsec;
tk->tkr.base_mono = ns_to_ktime(nsec);
/* Update the monotonic raw base */
tk->base_raw = timespec64_to_ktime(tk->raw_time);
}

tk->tkr.base_mono比tk->xtime_sec多了
tk->xtime_sec =>tk->xtime_sec
tk->tkr.base_mono =>(s64)(tk->xtime_sec + tk->wall_to_monotonic.tv_sec)*NSEC_PER_SEC+wall_to_monotonic.tv_nsec

多了wall_to_monotonic
我只要拿到xtime就好了,所以要用getnstimeofday64